import fusion/matching
import std/httpclient
import std/json
import std/os
import std/strformat
import std/strutils
import std/terminal

{.experimental: "caseStmtMacros".}

type
  CredentialsKind = enum
    ckUsernamePassword
    ckKey
  Credentials = object
    case kind: CredentialsKind
    of ckUsernamePassword:
      username: string
      password: string
    of ckKey:
      key: string

proc neocitiesRequest(credentials: Credentials; httpMethod: HttpMethod; endpoint: string; multipart: MultipartData = nil): JsonNode =
  let client = newHttpClient()
  var url: string
  case credentials
  of UsernamePassword(username: @username, password: @password):
    url = &"https://{username}:{password}@neocities.org/api/{endpoint}"
  of Key(key: @key):
    url = &"https://neocities.org/api/{endpoint}"
    client.headers = newHttpHeaders {"Authorization": &"Bearer {key}"}
  let response = client.request(url, httpMethod, multipart = multipart)
  result = response.body.parseJson

proc neocitiesUpload*(credentials: Credentials; paths: seq[string]): JsonNode =
  var multipart = newMultipartData()
  for path in paths:
    multipart[path] = (path, "", readFile(path))
  result = neocitiesRequest(credentials, HttpPost, "upload", multipart)

proc neocitiesInfo*(credentials: Credentials): JsonNode =
  result = neocitiesRequest(credentials, HttpGet, "info")

when isMainModule:
  import cligen

  proc displayMessage(response: JsonNode) =
    let responseType = response{"result"}.getStr
    let message = response{"message"}.getStr
    let (stream, color) = case responseType
    of "success":
      (stdout, fgGreen)
    of "error":
      (stderr, fgRed)
    else:
      (stderr, fgYellow)
    if stream.isatty:
      stream.styledWriteLine color, responseType.capitalizeAscii, ": ", resetStyle, message
    else:
      stream.writeLine responseType.capitalizeAscii, ": ", message

  template getCredentials() =
    let credentials {.inject.} = if key != "":
      Credentials(kind: ckKey, key: key)
    elif username != "" and password != "":
      Credentials(kind: ckUsernamePassword, username: username, password: password)
    else:
      try:
        Credentials(kind: ckKey, key: readFile(getConfigDir()/"neocities"/"config"))
      except IOError:
        stderr.writeLine "ERROR: No login credentials were given or found"
        quit QuitFailure

  proc upload(key = "", username = "", password = "", paths: seq[string]) =
    getCredentials()
    let response = neocitiesUpload(credentials, paths)
    response.displayMessage

  proc info(key = "", username = "", password = "") =
    getCredentials()
    let response = neocitiesInfo(credentials)
    response.displayMessage
    if stdout.isatty:
      for key, value in response{"info"}:
        stdout.styledWriteLine fgCyan, key.capitalizeAscii, ": ", resetStyle, value.getStr
    else:
      for key, value in response{"info"}:
        stdout.writeLine key.capitalizeAscii, ": ", value.getStr

  dispatchMulti([upload], [info])
